package spring.bean;

import spring.annotate.Bean;
import spring.annotate.Di;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationApplicationContext0 implements ApplicationContext0 {
    private static String rootPath;
    //    创建map集合 放bean对象
    private static Map<Class, Object> beanFactory0 = new HashMap<>();

    //设置包扫描规则
    //  哪个有bean注解，进行反射实例化
    //创建有参数构造
    public AnnotationApplicationContext0(String basePackage) throws Exception {
        //获取包的路径与包的扫描
        try {
            // 把.替换成/
            String packagePath = basePackage.replaceAll("\\.", "\\\\");
            //获取包的绝对路径
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
                    .getResources(packagePath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //包路径截取
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                //包的扫描的调用
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //属性注入
        loadDi();
    }

    //包的扫描
    private static void loadBean(File file) throws Exception {
        //判断是否为文件夹
        if (file.isDirectory()) {
            //获取文件夹内所有内容
            File[] childrenFiles = file.listFiles();
            //判断文件夹是否为空
            if (null == childrenFiles || childrenFiles.length == 0) {
                return;
            }
            //遍历文件夹里所有内容，如果是文件夹递归判断
            for (File file1 : childrenFiles) {
                //是文件时
                if (file1.isDirectory()) {
                    loadBean(file1);
                } else {
                    //得到包路径+类名
                    String pathWithClass =
                            file1.getAbsolutePath().substring(rootPath.length() - 1);
                    //文件是否为.class文件
//                    创建对象用字节码文件
                    if (pathWithClass.contains(".class")) {
                        //路径中\替换成.  把.class去掉
                        String allName =
                                pathWithClass.replaceAll("\\\\", ".")
                                        .replace(".class", "");
                        //判断类上是否有@Bean注解 并实例化
                        Class<?> clazz = Class.forName(allName);
                        if (!clazz.isInterface()) {
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if (annotation != null) {
                                Object instance1 = clazz.getDeclaredConstructor().newInstance();
                                //放到Map计划
                                //判断如果有接口 接口class作为map的key
                                if (clazz.getInterfaces().length > 0) {
                                    beanFactory0.put(clazz.getInterfaces()[0], instance1);
                                } else {
                                    beanFactory0.put(clazz, instance1);
                                }
                            }

                        }
                    }
                }
            }
        }
    }


    private void loadDi() throws Exception {
        //实例化对象在beanFactory的map集合里
        //遍历beanFactory的map集合
            //Map的entrySet()方法返回一个实现Map.Entry接口的对象集合。
            //集合中每个对象都是底层Map中一个特定的键/值对。
            //通过这个集合的迭代器，获得每一个条目(唯一获取方式)的键或值并对值进行更改。
                                                         //key Value关系
        Set<Map.Entry<Class, Object>> entries = beanFactory0.entrySet();
        for (Map.Entry<Class, Object> entry : entries) {
            //获取map集合每个对象值
            Object obj = entry.getValue();
            //获取对象Class
            Class<?> clazz = obj.getClass();
            //获得每个对象属性
            Field[] declareFields = clazz.getDeclaredFields();
            //遍历得到每个对象属性数组  得到每个属性
            for (Field field : declareFields) {
                //判断属性书否有@di注解
                Di annotation = field.getAnnotation(Di.class);
                if (null != annotation) {
                    //私有属性设置可访问
                    field.setAccessible(true);
                    //把对象进行注入
                    try {               //↓根据类型找到对象
                        field.set(obj, beanFactory0.get(field.getType()));
                    }catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    //返回对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory0.get(clazz);
    }

}
